﻿using Balloonerds.Algebra;
using System.Diagnostics;

namespace RayTracer
{
    public abstract partial class Thing
    {
        /// <summary>Packed spheres arranged in an octahedron pattern</summary>
        /// <param name="surface"></param>
        /// <returns></returns>
        public static Thing Tetrahedron(Surface surface)
        {
            var halfSide = 2 * System.Math.Sqrt(2) / 2;
            var boundaryRadius = 2 * halfSide * System.Math.Sqrt(3d / 8) + halfSide;

            var union = new Union() { Boundary = new Sphere() { Center = Vector.Origin, Radius = boundaryRadius } };

            union.Objects.Add(new Sphere() { Center = new Vector(+1, +1, +1), Radius = halfSide, Surface = surface });
            union.Objects.Add(new Sphere() { Center = new Vector(-1, -1, +1), Radius = halfSide, Surface = surface });
            union.Objects.Add(new Sphere() { Center = new Vector(-1, +1, -1), Radius = halfSide, Surface = surface });
            union.Objects.Add(new Sphere() { Center = new Vector(+1, -1, -1), Radius = halfSide, Surface = surface });

            return union;
        }

        /// <summary>Packed spheres arranged in an octahedron pattern</summary>
        /// <param name="surface"></param>
        /// <returns></returns>
        public static Thing Octahedron(Surface surface)
        {
            var halfSide = System.Math.Sqrt(2) / 2;
            var boundaryRadius = 2 * halfSide * System.Math.Sqrt(2) / 2 + halfSide;

            var union = new Union() { Boundary = new Sphere() { Center = Vector.Origin, Radius = boundaryRadius } };

            foreach (var i in new double[] { -1.0, 1.0 })
            {
                union.Objects.Add(new Sphere() { Center = new Vector(i, 0, 0), Radius = halfSide, Surface = surface });
                union.Objects.Add(new Sphere() { Center = new Vector(0, i, 0), Radius = halfSide, Surface = surface });
                union.Objects.Add(new Sphere() { Center = new Vector(0, 0, i), Radius = halfSide, Surface = surface });
            }

            return union;
        }

        /// <summary>Packed spheres arranged in a cube pattern</summary>
        /// <param name="surface"></param>
        /// <returns></returns>
        public static Thing Cube(Surface surface)
        {
            var halfSide = 1;
            var boundaryRadius = 2 * halfSide * System.Math.Sqrt(3) / 2 + halfSide;

            var union = new Union() { Boundary = new Sphere() { Center = Vector.Origin, Radius = boundaryRadius } };

            for (var i = 1; i <= 8; i++)
            {
                var vertex = new Vector(
                        (i & 1) > 0 ? 1 : -1,
                        (i & 2) > 0 ? 1 : -1,
                        (i & 4) > 0 ? 1 : -1
                        );
                union.Objects.Add(new Sphere() { Center = vertex, Radius = halfSide, Surface = surface });
            }
            return union;
        }

        /// <summary>Packed spheres arranged in an icosahedron pattern</summary>
        /// <param name="surface"></param>
        /// <returns></returns>
        public static Thing Icosahedron(Surface surface)
        {
            var phi = (1 + System.Math.Sqrt(5)) / 2;
            var halfSide = 1;
            var boundaryRadius = 2 * halfSide * System.Math.Sin(2 * System.Math.PI / 5) + halfSide;

            var union = new Union() { Boundary = new Sphere() { Center = Vector.Origin, Radius = boundaryRadius } };

            foreach (var i in new double[] { -1, 1 })
                foreach (var j in new double[] { -phi, phi })
                {
                    union.Objects.Add(new Sphere() { Center = new Vector(0, i, j), Radius = halfSide, Surface = surface });
                    union.Objects.Add(new Sphere() { Center = new Vector(i, j, 0), Radius = halfSide, Surface = surface });
                    union.Objects.Add(new Sphere() { Center = new Vector(j, 0, i), Radius = halfSide, Surface = surface });
                }

            return union;
        }

        /// <summary>Packed spheres arranged in a pentagonal dodecahedron pattern</summary>
        /// <param name="surface"></param>
        /// <returns></returns>
        public static Thing Dodecahedron(Surface surface)
        {
            var phi = (1 + System.Math.Sqrt(5)) / 2;
            var halfSide = 1 / phi;
            var boundaryRadius = System.Math.Sqrt(3) + halfSide;

            var union = new Union() { Boundary = new Sphere() { Center = Vector.Origin, Radius = boundaryRadius } };

            for (var i = 1; i <= 8; i++)
            {
                var vertex = new Vector(
                        (i & 1) > 0 ? 1 : -1,
                        (i & 2) > 0 ? 1 : -1,
                        (i & 4) > 0 ? 1 : -1
                        );
                union.Objects.Add(new Sphere() { Center = vertex, Radius = halfSide, Surface = surface });
            }

            foreach (var i in new double[] { -1.0 / phi, 1.0 / phi })
                foreach (var j in new double[] { -phi, phi })
                {
                    union.Objects.Add(new Sphere() { Center = new Vector(0, i, j), Radius = halfSide, Surface = surface });
                    union.Objects.Add(new Sphere() { Center = new Vector(i, j, 0), Radius = halfSide, Surface = surface });
                    union.Objects.Add(new Sphere() { Center = new Vector(j, 0, i), Radius = halfSide, Surface = surface });
                }

            return union;
        }

        public static Thing CheckeredFloor(double size)
        {
            return new Plane()
                {
                    Origin = new Vector(0, 0, 0),
                    Normal = Vector.yAxis,
                    Surface = Surface.CheckeredXZ(size)
                };
        }

        public static Thing Floor(Color color)
        {
            return new Plane()
            {
                Origin = new Vector(0, 0, 0),
                Normal = Vector.yAxis,
                Surface = Surface.Shiny(color)
            };
        }
    }
}
